package com.dingup.mock.module;

import com.alibaba.fastjson.JSONObject;
import com.dingup.api.Util.RedisUtils;
import com.dingup.mock.bean.exam.ExamMonitorData;
import com.dingup.mock.bean.exam.ExaminationInfo;
import com.dingup.mock.bean.exam.TsExamRecord;
import com.dingup.mock.cache.ExamCache;
import com.dingup.mock.dao.exam.TsExamRecordsMapper;
import com.dingup.mock.enums.ExamRecordTypeEnum;
import com.dingup.util.GDateUtil;
import com.google.gson.Gson;
import com.suyeer.basic.util.BJsonUtil;

import java.util.*;

import static com.dingup.mock.util.ConstUtil.*;


/**
 * Created by yanggavin on 16/3/16.
 */
public class SExaminationQueueCache {
    private static class LazyHolder {
        private static final SExaminationQueueCache INSTANCE = new SExaminationQueueCache();
    }

    public static SExaminationQueueCache getInstance() {
        return LazyHolder.INSTANCE;
    }

    private SExaminationQueueCache() {
    }

    /**
     * 添加新的考试记录
     *
     * @param examInfo 考试信息
     */
    public void addItem(ExaminationInfo examInfo) {
        // 获取最新的考试ID列表
        List<String> curExamIdList = getCurExamIdListFromMc();
        // 添加新的考试ID
        if (!curExamIdList.contains(examInfo.getId())) {
            curExamIdList.add(examInfo.getId());
            // 添加到MC
            ExamCache.getInstance().addOrReplace(
                    examInfo.getId(),
                    examInfo,
                    MC_EXAM_RECORD_EXPIRY_SECOND
            );
            // 保存当前考试ID列表到MC
            syncProgressExamIdListToMc(curExamIdList);
        }
    }

    /**
     * 检查考试Id是否有效存在
     *
     * @param examId
     * @return
     */
    public Boolean containsExamId(String examId) {
        return getCurExamIdListFromMc().contains(examId);
    }

    /**
     * 获取考试实例部分(带是否从MC同步数据配置)
     *
     * @param examId
     * @return
     */
    public ExaminationInfo getExaminationInfo(String examId) {

        return (ExaminationInfo) ExamCache.getInstance().getValue(examId, ExaminationInfo.class);
    }

    /**
     * 获取当前缓存队列数
     *
     * @return
     */
    public int getCurCacheSize() {
        return getCurExamIdListFromMc().size();
    }

    /**
     * 删除实例缓存
     *
     * @param id
     */
    public void removeExaminationInfo(String id) {
        // 获取最新的考试ID列表
        List<String> curExamIdList = getCurExamIdListFromMc();
        if (curExamIdList.contains(id)) {
            curExamIdList.remove(id);
            // remove mc data.
            ExamCache.getInstance().del(id);
            // 保存当前考试ID列表到MC
            syncProgressExamIdListToMc(curExamIdList);
        }
    }

    /**
     * 获取当前缓存队列开始时间列表
     *
     * @return
     */
    public List<ExamMonitorData> getCurCacheDataList() {
        List<ExamMonitorData> retList = new ArrayList<>();
        for (ExaminationInfo ei : getCurExamDataList()) {
            ExamMonitorData emd = new ExamMonitorData();
            emd.setUserId(ei.getUserId());
            emd.setExamId(ei.getId());
            emd.setSubjectName(ei.getSubjectName());
            emd.setExamType(ei.getExamType());
            emd.setExamContentType(ei.getExamContentType());
            emd.setBeginTime(GDateUtil.getDateStr(ei.getBeginTime()));
            emd.setCurProcess(ei.getCurAnswerProcess());
            emd.setLatestAnswerTime(GDateUtil.getDateStr(ei.getLastUpdateTime()));
            emd.setUserName(RedisUtils.getInstance().getUserName(ei.getUserAccount()));
            emd.setUserName("test data");
            emd.setSpendTime(GDateUtil.getSecondResult4SubTime(ei.getBeginTime(), ei.getLastUpdateTime()) / 60);
            retList.add(emd);
        }
        // 按照最新作答时间倒序排列
        Collections.sort(retList, new Comparator<ExamMonitorData>() {
            @Override
            public int compare(ExamMonitorData o1, ExamMonitorData o2) {
                return o2.getLatestAnswerTime().compareTo(o1.getLatestAnswerTime());
            }
        });
        return retList;
    }

    /**
     * 自动清理超时的缓存项
     */
    public void clearExpiredExaminationInfos(TsExamRecordsMapper tsExamRecordsMapper) throws Exception {
        Map<String, ExaminationInfo> expiredListMap = new HashMap<>();
        for (ExaminationInfo ei : getCurExamDataList()) {
            if (GDateUtil.checkExpired(ei.getLastUpdateTime(), EXAM_QUEUE_AUTO_CLEAR_EXPIRED_SECOND)) {
                expiredListMap.put(ei.getId(), ei);
            }
        }
        // 将超时的考试实例入库,但标记为过期状态
        for (String id : expiredListMap.keySet()) {
            saveExpiredExamRecordToDB(tsExamRecordsMapper, expiredListMap.get(id));
        }
        // 清理超时的考试缓存数据
        for (String id : expiredListMap.keySet()) {
            removeExaminationInfo(id);
        }
    }

    /**
     * 根据作业Id获取当前进行中的模考进度信息
     *
     * @param homeworkId
     * @return
     */
    public Map<String, JSONObject> getCurExamDataListByHomeworkId(Integer homeworkId) {
        Map<String, JSONObject> retMap = new HashMap<>();
        // 根据HomeworkId来过滤相关考试进程
        for (ExaminationInfo ei : getCurExamDataList()) {
            if (ei.getHomeworkId().equals(homeworkId)) {
                JSONObject jsonObj = new JSONObject();
                jsonObj.put("examId", ei.getId());
                jsonObj.put("homeworkQuestionNum", ei.getHomeworkQuestionNum());
                jsonObj.put("curAnswerProcess", ei.getCurAnswerProcess());
                jsonObj.put("beginTime", ei.getBeginTime());
                jsonObj.put("lastUpdateTime", ei.getLastUpdateTime());
                retMap.put(ei.getUserId(), jsonObj);
            }
        }
        return retMap;
    }

    /**
     * 根据用户ID获取该用户正在进行中的模考数据（非作业）
     *
     * @param userId
     * @return
     */
    public ExaminationInfo getExamCacheDataByUserId(String userId) {
        for (ExaminationInfo ei : getCurExamDataList()) {
            ExaminationInfo examinationInfo = BJsonUtil.changeType(ei, ExaminationInfo.class);
            if (examinationInfo.getHomeworkId() == 0 && userId.equals(examinationInfo.getUserId())) {
                return ei;
            }
        }
        return null;
    }

    /**
     * 获取某个用户某次作业相关的模考缓存记录（作业）
     *
     * @param userId
     * @param homeworkId
     * @return
     */
    public List<ExaminationInfo> getExamCacheDataByUserIdAndHomeworkId(String userId, Integer homeworkId) {
        List<ExaminationInfo> retList = new ArrayList<>();
        for (ExaminationInfo ei : getCurExamDataList()) {
            // 搜索用户ID和作业ID匹配的缓存记录，并提取
            if (ei.getHomeworkId().equals(homeworkId) && ei.getUserId().equals(userId)) {
                retList.add(ei);
            }
        }
        return retList;
    }

    /**
     * 保存过期的考试信息到数据库
     *
     * @param tsExamRecordsMapper
     * @param ei
     */
    private void saveExpiredExamRecordToDB(TsExamRecordsMapper tsExamRecordsMapper, ExaminationInfo ei) throws Exception {
        TsExamRecord ter = new TsExamRecord();
        ter.setExamId(ei.getId());
        ter.setSubjectName(ei.getSubjectName());
        ter.setUserId(ei.getUserId());
        ter.setExamType(ei.getExamType());
        ter.setExamContentType(ei.getExamContentType());
        ter.setBeginTime(ei.getBeginTime());
        ter.setEndTime(ei.getEndTime());
        ter.setExamSummaryInfo("");
        ter.setHomeworkRecordId(ei.getHomeworkRecordId());
        ter.setHomeworkQuestionNum(ei.getHomeworkQuestionNum());
        // 记录状态和考试实例数据,为过期数据独有
        ter.setRecordType(ExamRecordTypeEnum.EXPIRED);
        ter.setExpiredExamInfo((new Gson()).toJson(ei));
        if (!tsExamRecordsMapper.add(ter)) {
            throw new Exception("保存过期考试实例数据失败");
        }
    }

    /**
     * 同步数据到MC
     *
     * @param id
     */
    public void syncDataToMC(String id, ExaminationInfo ei) {
        ExamCache.getInstance()
                .addOrReplace(id, ei, MC_EXAM_RECORD_EXPIRY_SECOND);
    }

    /**
     * 将当前进行中的考试ID保存到MC
     */
    private void syncProgressExamIdListToMc(List<String> examIdList) {
        ExamCache.getInstance().addOrReplace(
                MC_EXAM_CURRENT_EXAM_ID_LIST,
                examIdList,
                MC_EXAM_RECORD_EXPIRY_SECOND);
    }

    /**
     * 从MC获取最新的考试ID列表到本地
     */
    private List<String> getCurExamIdListFromMc() {
        List<String> examIdList = (List<String>) ExamCache.getInstance().getValue(MC_EXAM_CURRENT_EXAM_ID_LIST);
        return (examIdList == null ? new ArrayList<String>() : examIdList);

    }

    /**
     * 获取当前全部的含全部考试信息的列表
     *
     * @return
     */
    private List<ExaminationInfo> getCurExamDataList() {
        List<ExaminationInfo> curExamDataList = new ArrayList<>();
        List<String> expiredExamIdList = new ArrayList<>();
        List<String> curExamIdList = getCurExamIdListFromMc();
        for (String examId : curExamIdList) {
            if (ExamCache.getInstance().containKey(examId)) {
                curExamDataList.add(ExamCache.getInstance().getValue(examId, ExaminationInfo.class));
            } else {
                expiredExamIdList.add(examId);
            }
        }
        // 如果存在过期的考试ID，需要同步到MC
        if (expiredExamIdList.size() > 0) {
            for (String id : expiredExamIdList) {
                curExamIdList.remove(id);
            }
            syncProgressExamIdListToMc(curExamIdList);
        }
        return curExamDataList;
    }
}
